Scenario #9640: A Business Api Can Be Used with a Valid Api Key Acting As Global Admin

Any regular business API, here GET /api/hs/office/memberships as an example, can be used with a valid API-key in the Hostsharing-Api-Key header instead of a Keycloak OIDC JWT. The API-key acts as its API_KEY subject with whatever roles got granted to it, here the global ADMIN role, which can view all memberships.

Properties

Given

name value
subjectUuid a91c0004-0000-0000-0000-000000000004
subjectName reporting.key

API_KEY subjects authenticate technical clients via the Hostsharing-Api-Key HTTP header instead of a Keycloak OIDC JWT, e.g. automation programs, completely bypassing Keycloak. Only a global-admin may create API_KEY subjects. The clear-text API-key is returned only once, in the response of creating the API_KEY subject; just its hash gets stored. Like GROUP subjects, API_KEY subjects cannot have an account. Global API_KEY subjects do not belong to a realm, thus their name must neither contain a - (the realm-prefix delimiter) nor a / (the GROUP subject marker).

Create the API_KEY Subject

The response contains the generated clear-text API-key (property apiKey) exactly once; it cannot be retrieved again.

HTTP POST "/api/rbac/subjects" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  <<EOF
{
  "uuid" : "a91c0004-0000-0000-0000-000000000004",
  "name" : "reporting.key",
  "type" : "API_KEY"
}
EOF
=> status: 201 CREATED a91c0004-0000-0000-0000-000000000004
{
  "uuid" : "a91c0004-0000-0000-0000-000000000004",
  "name" : "reporting.key",
  "organization" : "reporting",
  "type" : "API_KEY",
  "apiKey" : "hsak_reporting.key.b20347578485b052f1dd35a0357ae7ced6d57c09021aeca20ebf8e03ab4fefb8",
  "scopes" : null,
  "expiresAt" : null
}

Prerequisite: Resolve the UUID of the global ADMIN role

The grant API needs the UUID of the role which we want to grant.

HTTP GET "/api/rbac/roles?name=rbac.global%23global%3AADMIN" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }`
=> status: 200 OK 
[ {
  "uuid" : "7ea7e6cb-16dd-4fb9-aa82-427df5f205d7", // globalAdminRoleUuidToGrant
  "object.uuid" : "6ec9aacc-2bd3-4678-a8f9-31e61eef3b0a",
  "objectTable" : "rbac.global",
  "objectIdName" : "global",
  "roleType" : "ADMIN",
  "roleName" : "rbac.global#6ec9aacc-2bd3-4678-a8f9-31e61eef3b0a:ADMIN",
  "roleIdName" : "rbac.global#global:ADMIN"
} ]

Grant the global ADMIN role to the API_KEY Subject

HTTP POST "/api/rbac/grants" \
  -H "Authorization: Bearer $HSADMINNG_JWT_BEARER" \
  `# {` \
  `#   "sub" : "uuid<hsh-alex_superuser>"` \
  `# }` \
  -H 'Hostsharing-Assumed-Roles: rbac.global#global:ADMIN' \
  <<EOF
{
  "assumed" : true,
  "grantedRole.uuid" : "7ea7e6cb-16dd-4fb9-aa82-427df5f205d7", // globalAdminRoleUuidToGrant
  "granteeSubject.uuid" : "a91c0004-0000-0000-0000-000000000004"
}
EOF
=> status: 201 CREATED 7ea7e6cb-16dd-4fb9-aa82-427df5f205d7 // globalAdminRoleUuidToGrant

Verify the API-key authenticates as its Subject with the global-admin role, without any JWT

HTTP GET "/api/hs/accounts/current" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
{
  "subject" : {
    "uuid" : "a91c0004-0000-0000-0000-000000000004",
    "name" : "reporting.key",
    "organization" : "reporting",
    "type" : "API_KEY"
  },
  "person" : null,
  "globalAdmin" : true
}

List arbitrary memberships via the business API, authenticated just by the API-key

HTTP GET "/api/hs/office/memberships?partnerNumber=P-10001" \
  -H "Hostsharing-Api-Key: $HSADMINNG_API_KEY"
=> status: 200 OK 
[ {
  "uuid" : "44efcd04-e400-4134-a776-96fa9081a44d",
  "partner" : {
    "uuid" : "3b00b421-19be-4f54-af72-8071cd2db54b",
    "partnerNumber" : "P-10001",
    "partnerRel" : {
      "uuid" : "e23bb6dc-c4c4-4d38-9c61-cea341cd463d",
      "anchor" : {
        "uuid" : "806e75fe-7894-4422-86be-783107f07f3f",
        "personType" : "LEGAL_PERSON",
        "tradeName" : "Hostsharing eG",
        "salutation" : null,
        "title" : null,
        "givenName" : null,
        "familyName" : null
      },
      "holder" : {
        "uuid" : "f726eb70-6779-41eb-a327-09e132a0cdd5", // Person: First GmbH
        "personType" : "LEGAL_PERSON",
        "tradeName" : "First GmbH",
        "salutation" : null,
        "title" : null,
        "givenName" : null,
        "familyName" : null
      },
      "type" : "PARTNER",
      "mark" : null,
      "contact" : {
        "uuid" : "57cefbf1-4b36-48b3-8400-2e027035af9a", // Contact: contact-admin@firstcontact.example.com
        "caption" : "first contact",
        "postalAddress" : {
          "country" : "Germany"
        },
        "emailAddresses" : {
          "main" : "contact-admin@firstcontact.example.com"
        },
        "phoneNumbers" : {
          "phone_office" : "+49 123 1234567"
        }
      }
    },
    "details" : {
      "uuid" : "999bc911-fd9d-4299-aa18-77938c268c98",
      "registrationOffice" : "Hamburg",
      "registrationNumber" : "RegNo123456789",
      "birthName" : null,
      "birthPlace" : null,
      "birthday" : null,
      "dateOfDeath" : null
    }
  },
  "mainDebitor" : null,
  "memberNumber" : "M-1000101",
  "memberNumberSuffix" : "01",
  "validFrom" : "2022-10-01",
  "validTo" : "2024-12-30",
  "status" : "CANCELLED",
  "membershipFeeBillable" : true
} ]

generated on 2026-08-10 01:38:06 for branch